'use strict'
const mediasoupClient = require('mediasoup-client');
const bowser  = require('bowser');
const SocketQueue = require('./queue');

const url = "https://doctor.jialianiot.com/api/get_member";
const queue = new SocketQueue();

const VIDEO_CONSTRAINS = {
	qvga : { width: { ideal: 320 }, height: { ideal: 240 } },
	vga  : { width: { ideal: 640 }, height: { ideal: 480 } },
	hd   : { width: { ideal: 1280 }, height: { ideal: 720 } }
};

// 浏览器供应商的专有约束
const PC_PROPRIETARY_CONSTRAINTS = {	
	optional : [ { googDscp: true } ]
};

// 用于同步播放网络摄像机视频
const WEBCAM_SIMULCAST_ENCODINGS =
[
	{ scaleResolutionDownBy: 4, maxBitrate: 500000 },
	{ scaleResolutionDownBy: 2, maxBitrate: 1000000 },
	{ scaleResolutionDownBy: 1, maxBitrate: 5000000 }
];

// 用于VP9网络摄像头视频
const WEBCAM_KSVC_ENCODINGS =
[
	{ scalabilityMode: 'S3T3_KEY' }
];

// 用于同播屏幕共享
const SCREEN_SHARING_SIMULCAST_ENCODINGS =
[
	{ dtx: true, maxBitrate: 1500000 },
	{ dtx: true, maxBitrate: 6000000 }
];

// 用于VP9屏幕共享
const SCREEN_SHARING_SVC_ENCODINGS =
[
	{ scalabilityMode: 'S3T3', dtx: true }
];

// 使用数据通道
const useDataChannel = false;


var localVideo = document.querySelector('video#localvideo');
var remoteVideo = document.querySelector('video#remotevideo');

var conButton =  document.querySelector('button#con');
var yesButton =  document.querySelector('button#yes');
var noButton = document.querySelector('button#no');
var setMaxSpatialLayerButton = document.querySelector('button#SpatialLayer_button');
var setPreferredLayersButton = document.querySelector('button#PreferredLayers_button');

var tokenInput = document.querySelector('input#token');
var other_uidInput = document.querySelector('input#other_uid');
var other_client_listInput = document.querySelector('input#other_client_list');
var setMaxSpatialLayerInput = document.querySelector('input#SpatialLayer_input');
var setPreferredLayersInput = document.querySelector('input#PreferredLayers_input');

var messageTextarea = document.querySelector('textarea#message');
var localvideo_messageTextarea = document.querySelector('textarea#localvideo_message');
var remotevideo_messageTextarea = document.querySelector('textarea#remotevideo_message');


var mediasoupDevice = null;


var localStream = null;
var remoteStream = null;

var sendTransport = null;
var recvTransport = null;

var micProducer = null;
var webcamProducer = null;
var shareProducer = null;

var externalVideo = null;
var externalVideoStream = null;

var webcams = new Map();
var consumers = new Map();
var dataConsumers = new Map();
var webcam ={
	device     : null,
	resolution : 'hd' //hd
};


var forceH264 = Boolean(false);
var forceVP9 = Boolean(false);
var useSimulcast = Boolean(true);

var uesername;
var socket;
var roomid = "";
var server_id = "";
var userprofile = new Object;
var other_uid;
var other_client_list;
var timeoutHangUpId;





conButton.onclick = () => {
	//remoteStream = new MediaStream;
	userprofile.token = tokenInput.value;
	connectWebSocket();

	$.ajax({
        type: 'POST',
        url: url,
		data:{
			token:userprofile.token
		},
        dataType: "json",
        async: false,
        success: function (result) {
			console.log(result);
			userprofile.realname = result.data.realname;
        },
		error: function(){
			console.log('网络异常');
		}
    })
};
yesButton.onclick = sendVideoRequest;

setMaxSpatialLayerButton.onclick = () => {
	//在联播的情况下，此方法限制传输到服务器的最高 RTP 流
	if (webcamProducer)
		webcamProducer.setMaxSpatialLayer(Number(setMaxSpatialLayerInput.value));
};
setPreferredLayersButton.onclick = () => {
	//设置要发送到消费端点的首选（最高）空间和时间层。仅对联播和 SVC 消费者有效
	//if (consumerId)
	//	await setConsumerPreferredLayers(consumerId, spatialLayer, temporalLayer);
};



async function connectWebSocket() {
	socket = new WebSocket('wss://ceic.jialianiot.com:8286');

	socket.addEventListener('open', handleSocketOpen);
	socket.addEventListener('message', handleSocketMessage);
	socket.ad dEventListener('error', handleSocketError);
	socket.addEventListener('close', handleSocketClose);
};


async function handleSocketOpen() {
	console.log('handleSocketOpen()');
	messageTextarea.value = messageTextarea.value + "1Socket登录成功" + '\r\n';
	socket.send(JSON.stringify({"type":"app_login", "token":userprofile.token}));
};

async function handleSocketMessage(message) {
	try {
		const jsonMessage = JSON.parse(message.data);
		handleJsonMessage(jsonMessage);
	  } catch (error) {
		console.error('handleSocketMessage() failed [error:%o]', error);
	  }
};

async function handleSocketError() {
	console.error('handleSocketError() [error:%o]', error);
};

async function handleSocketClose() {
	console.log('handleSocketClose()');

	if (sendTransport)
	{
		sendTransport.close();
		sendTransport = null;
	}

	if (recvTransport)
	{
		recvTransport.close();
		recvTransport = null;
	}
};

//处理Socket消息
async function handleJsonMessage(jsonMessage) {
	console.log('SocketMessage：[error:%o]', jsonMessage);
	const { type } = jsonMessage;

	switch (type) {
		case 'app_login':	//返回登录信息

			userprofile.client_id = jsonMessage.client_id;
			userprofile.id = jsonMessage.id;
			userprofile.status = 1;

			break;
		
		case 'app_inquiry_server_reply':	//请求视频连接服务器回复

			if( jsonMessage.code == "1001" ){
				messageTextarea.value = messageTextarea.value + '尝试呼叫，请等待对方应答' + '\r\n';
				noButton.onclick = cancelVideoRequest;
				timeoutHangUpId = setTimeout(cancelVideoRequest,60000);
			}else if( jsonMessage.code == "4001" ){
				messageTextarea.value = messageTextarea.value + '对方忙线中' + '\r\n';
				userprofile.status = 1;
			}else if( jsonMessage.code == "4002" ){
				messageTextarea.value = messageTextarea.value + '对方已下线' + '\r\n';
				userprofile.status = 1;
			}

			break;

		case 'byebye_client_id':	//对方中途取消了视频连接请求

			messageTextarea.value = messageTextarea.value + '对方已经取消了视频连接请求' + '\r\n';
			userprofile.status = 1;			

			break;

		case 'app_inquiry':		//接收到视频连接请求

			if(userprofile.status == 2){
				return;
			}
			other_client_list = jsonMessage.from;
			other_uid = jsonMessage.from_id;
			messageTextarea.value = messageTextarea.value + jsonMessage.realname + '，1请求与您建立视频通讯，是否同意？' + '\r\n';
			userprofile.status = 2;
			yesButton.onclick = receiveVideoRequest;
			noButton.onclick = rejectVideoRequest;

			break;

		case 'feedback':	//对方是否同意接收请求

			clearInterval(timeoutHangUpId);
			if( jsonMessage.isagree == "1" ){
				messageTextarea.value = messageTextarea.value + '对方已同意您的视频连接请求，正在建立连接，请稍后...' + '\r\n';
				var jjj = JSON.stringify({"type":"sendurl", "server_id":server_id, "inquiry_type":"1", "client_list":jsonMessage.from, "groupid":roomid, "realname":userprofile.realname, "send_type":"1",
                    "uid":other_uid, "from_id":userprofile.id, "headimgurl":userprofile.headimgurl, "special_type":userprofile.special_type, "sos":"1"});
				messageTextarea.value = messageTextarea.value + jjj + '\r\n';
				socket.send(JSON.stringify({"type":"sendurl", "server_id":server_id, "inquiry_type":"1", "client_list":jsonMessage.from, "groupid":roomid, "realname":userprofile.realname, "send_type":"1",
					"uid":other_uid, "from_id":userprofile.id, "headimgurl":userprofile.headimgurl, "special_type":userprofile.special_type, "sos":"1"}));
			}else{
				messageTextarea.value = messageTextarea.value + jsonMessage.realname + '，拒绝了您的视频连接请求' + '\r\n';
				userprofile.status = 1;
			}

			break;

		case 'sendurl':	//创建房间

			server_id = jsonMessage.server_id;
			roomid = jsonMessage.groupid;
			initializationData();
			if( jsonMessage.send_type == "1" ){
				socket.send(JSON.stringify({"type":"sendurl", "server_id":server_id, "inquiry_type":"1", "client_list":jsonMessage.from, "groupid":roomid, "realname":userprofile.realname, "send_type":"2", 
				"uid":other_uid, "from_id":userprofile.id, "headimgurl":userprofile.headimgurl, "special_type":userprofile.special_type, "sos":"1" ,"special_type":0}));
			}
			if( jsonMessage.send_type == "2" ){					
			}
			socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"create_room", "roomId":roomid, "peerId":userprofile.id}}));

			break;

		case 're_server_data':	//视频流服务器信息

			const message = jsonMessage.data;
			switch (message.action) {
				case 'getRouterRtpCapabilities':	//获取路由器Rtp能力

					mediasoupDevice = new mediasoupClient.Device(
						//{ handlerName: "Chrome67" }	//强制内置处理程序之一的名称
					);
					const routerRtpCapabilities = message.routerRtpCapabilities;
					await mediasoupDevice.load({ routerRtpCapabilities });
					socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"peer_join","roomId":roomid,"peerId":userprofile.id,"device":deviceInfo(),
					"rtpCapabilities":mediasoupDevice.rtpCapabilities,"sctpCapabilities":mediasoupDevice.sctpCapabilities}}));					

					break;
					
				case 'peer_join':	//加入房间结果

					if( message.result == 'success' ){				
						socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"createWebRtcTransport", 
						"roomId":roomid, "peerId":userprofile.id, "forceTcp":false, "producing":true, "consuming":false,
						"sctpCapabilities":useDataChannel?mediasoupDevice.sctpCapabilities:undefined}}));			
					}else{
						messageTextarea.value = messageTextarea.value + message.meg + '\r\n';
					}

					break;

				case 'createWebRtcTransport':	//创建WebRtcTransport

					if( sendTransport == null ){
						await handleSendTransportListeners(message.id,message.iceParameters,message.iceCandidates,message.dtlsParameters);
						socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"createWebRtcTransport", 
						"roomId":roomid, "peerId":userprofile.id, "forceTcp":false, "producing":false, "consuming":true, 
						"sctpCapabilities":useDataChannel?mediasoupDevice.sctpCapabilities:undefined}}));

						enableMic();
						enableWebcam();
					}else{
						await handleRecvTransportListeners(message.id,message.iceParameters,message.iceCandidates,message.dtlsParameters,message.sctpParameters);
					}
					
					break;

				case 'connectWebRtcTransport':	//连接成功后发送produce

					console.log('connectWebRtcTransport()');
					let actionStr_connect;
					try {
						if(sendTransport.id === message.transportId){
							actionStr_connect = 'connect-sendTransport';
						}
						if(recvTransport.id === message.transportId){
							actionStr_connect = 'connect-recvTransport';
						}					
						const action = queue.get(actionStr_connect);
				
						if (!action) {
							throw new Error('transport-connect action was not found');
						}
						await action(message);
					} catch (error) {
						console.error('connectWebRtcTransport() failed [error:%o]', error);
					}
				
					// 创建消费者
					if( recvTransport.id == message.transportId ){
						setTimeout(async () => {
							socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"createConsumer", "roomId":roomid, "peerId":userprofile.id}}));
						}, 1000);
					}			

					break;

				case 'produce':	//produce返回

					console.log('produce()');
					let actionStr_produce;
					try {
						if(message.kind === "audio"){
							actionStr_produce = 'produce-audio';
						}
						if(message.kind === "video"){
							actionStr_produce = 'produce-video';
						}
						const action = queue.get(actionStr_produce);
				
						if (!action) {
							throw new Error('produce action was not found');
						}				
						await action(message);
					} catch (error) {
						console.error('produce() failed [error:%o]', error);
					}

					break;

				case 'newConsumer':
				
					try
					{
						const consumer = await recvTransport.consume(
							{
								id:message.id,
								producerId:message.producerId,
								kind:message.kind,
								rtpParameters:message.rtpParameters,
								appData:message.appData,
							});

						// 存储在map中
						consumers.set(consumer.id, consumer);

						consumer.on('transportclose', () =>
						{
							consumers.delete(consumer.id);
						});

						//显示对方视频						
						//const { track } = consumer;
						//remoteVideo.srcObject = new MediaStream([ track ]);
						//accept();
						remoteStream.addTrack(consumer.track);
						if(remoteStream.getTracks().length === 2){
							remoteVideo.srcObject = remoteStream;
						}
						socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"consumer", 
						"roomId":roomid, "peerId":userprofile.id, "consumerId":message.id, "result":"success"}}));						
						

						// 如果仅音频模式已启用，请暂停
						//if (consumer.kind === 'video' && store.getState().me.audioOnly)
						//	pauseConsumer(consumer);

					}
					catch (error)
					{
						console.log('"newConsumer" request failed:%o', error);
						throw error;
					}

					break;
				}

			break;		

		default: console.log('handleJsonMessage() unknown action %o', jsonMessage);
  	}
};


//发起呼叫
function sendVideoRequest() {
	other_client_list = other_client_listInput.value;
	other_uid = other_uidInput.value;
	socket.send(JSON.stringify({"type":"app_inquiry", "realname":userprofile.realname, "inquiry_type":"1", "client_list":other_client_list, "uid":other_uid, "from_id":userprofile.id}));
}

//取消视频请求
function cancelVideoRequest() {
	clearInterval(timeoutHangUpId);
	socket.send(JSON.stringify({"type":"byebye_client_id", "client_list":other_client_list, "uid":other_uid, "from_id":userprofile.id}));
	userprofile.status = 1;
	messageTextarea.value = messageTextarea.value + '视频呼叫已取消' + '\r\n';
}

//接收视频请求
function receiveVideoRequest() {
	socket.send(JSON.stringify({"type":"feedback", "realname":userprofile.realname, "inquiry_type":"2", "client_list":other_client_list, "isagree":"1", "uid":other_uid, "from_id":userprofile.id}));
}

//拒绝视频请求
function rejectVideoRequest() {
	socket.send(JSON.stringify({"type":"feedback", "realname":userprofile.realname, "inquiry_type":"2", "client_list":other_client_list, "isagree":"2", "uid":other_uid, "from_id":userprofile.id}));
	userprofile.status = 1;
}


//创建发送Transport
async function handleSendTransportListeners(id,iceParameters,iceCandidates,dtlsParameters){
	console.log('handleSendTransportListeners');
	sendTransport = mediasoupDevice.createSendTransport(	//创建一个新的WebRTC传输以发送媒体
		{
			id,
			iceParameters,
			iceCandidates,
			dtlsParameters
			//sctpParameters,
			//iceServers             : [],
			//proprietaryConstraints : PC_PROPRIETARY_CONSTRAINTS,
			//additionalSettings 	   : { encodedInsertableStreams: false }	//可插入流编码(好像与加密有关)
		});

	sendTransport.on(		//连接事件
		'connect', ({ dtlsParameters }, callback, errback) =>
		{
			console.log('sendTransport-connect');
			try	{
				const action = (jsonMessage) => {
					//console.log('connect-transport action');
					callback();
					queue.remove('connect-sendTransport');
				};
				queue.push('connect-sendTransport', action);
				
				socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"connectWebRtcTransport", "roomId":roomid, "peerId":userprofile.id, 
				"transportId":sendTransport.id, "dtlsParameters":dtlsParameters}}));
			}catch (error){
				errback(error);
			}
		});

	sendTransport.on(		//生产事件
		'produce', async ({ kind, rtpParameters, appData }, callback, errback) =>
		{
			console.log('sendTransport-produce');
			try	{
				let action;
				if(kind === 'audio'){
					action = (jsonMessage) => {
						//console.log('handleTransportProduceEvent callback [data:%o]', jsonMessage);
						callback({ id: jsonMessage.id });
						queue.remove('produce-audio');
					};			  
					queue.push('produce-audio', action);
				}
				if(kind === 'video'){
					action = (jsonMessage) => {
						//console.log('handleTransportProduceEvent callback [data:%o]', jsonMessage);
						callback({ id: jsonMessage.id });
						queue.remove('produce-video');
					};			  
					queue.push('produce-video', action);
				}

				socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"produce", "roomId":roomid, "peerId":userprofile.id, 
				"transportId":sendTransport.id, "kind":kind, "rtpParameters":rtpParameters, "appData":appData}}));				
			}catch (error){
				errback(error);
			}
		});

	sendTransport.on(
		'connectionstatechange', connectionState => {
			console.log('send transport connection state change [state:%s]', connectionState);
		});
}


//创建接收Transport
async function handleRecvTransportListeners(id,iceParameters,iceCandidates,dtlsParameters,sctpParameters){
	console.log('handleRecvTransportListeners');
	recvTransport = mediasoupDevice.createRecvTransport(
		{
			id,
			iceParameters,
			iceCandidates,
			dtlsParameters,
			sctpParameters
			//iceServers 	       : [],
			//additionalSettings : { encodedInsertableStreams: false }
		});

	recvTransport.on(
		'connect', ({ dtlsParameters }, callback, errback) =>
		{
			console.log('recvTransport-connect');
			try	{
				const action = (jsonMessage) => {
					//console.log('connect-transport action');
					callback();
					queue.remove('connect-recvTransport');
				};
				queue.push('connect-recvTransport', action);
				
				socket.send(JSON.stringify({"type":"get_server","server_id":server_id,"data":{"action":"connectWebRtcTransport", "roomId":roomid, "peerId":userprofile.id, 
				"transportId":recvTransport.id, "dtlsParameters":dtlsParameters}}));
			}catch (error){
				errback(error);
			}
		});
}


//启用麦克风
async function enableMic()	
{
	console.log('启用麦克风');

	if (micProducer)
		return;

	if (!mediasoupDevice.canProduce('audio'))
	{
		console.log('不支持音频编码');
		return;
	}

	let track;

	try
	{
		if (!externalVideo)
		{
			const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
			track = stream.getAudioTracks()[0];
		}
		else
		{
			const stream = await getExternalVideoStream();
			track = stream.getAudioTracks()[0].clone();
		}

		micProducer = await sendTransport.produce(
			{
				track,
				codecOptions :			//每个编解码器特定选项
				{
					opusStereo : 1,		//启用 OPUS 立体声
					opusDtx    : 1		//启用 OPUS 不连续传输
				}
			});

		micProducer.on('transportclose', () =>	//当transpor关闭
		{
			micProducer = null;
		});

		micProducer.on('trackended', () =>	//当正在传输的音频/视频轨道在外部停止时
		{
			//disableMic()
			//	.catch(() => {});
		});
	}
	catch (error)
	{
		console.log('麦克风不可用', error);

		if (track)
			track.stop();
	}
}


async function disableMic()	//禁用麦克风
{
	console.log('disableMic()');

	if (!micProducer)
		return;

	micProducer.close();

	try
	{
		await peer.request(
			'closeProducer', { producerId: micProducer.id });
	}
	catch (error)
	{
		console.log('Error closing server-side mic Producer:%o', error);
	}

	micProducer = null;
}


async function muteMic()	//静音麦克风 
{
	console.log('muteMic()');

	micProducer.pause();

	try
	{
		await peer.request(
			'pauseProducer', { producerId: micProducer.id });
	}
	catch (error)
	{
		console.log('muteMic() | failed: %o', error);
	}
}


async function unmuteMic()	//取消静音麦克风 
{
	console.log('unmuteMic()');

	micProducer.resume();

	try
	{
		await peer.request(
			'resumeProducer', { producerId: micProducer.id });
	}
	catch (error)
	{
		console.log('unmuteMic() | failed: %o', error);
	}
}


async function enableWebcam()	//启用网络摄像头 
{
	console.log('启用网络摄像头');

	if (webcamProducer)
		return;
	//else if (shareProducer)
	//	await disableShare();

	if (!mediasoupDevice.canProduce('video'))
	{
		console.log('不支持视频编码');
		return;
	}

	let track;
	let device;

	try
	{
		if (!externalVideo)
		{
			await updateWebcams();
			device = webcam.device;

			const { resolution } = webcam;

			if (!device)
				throw new Error('no webcam devices');

			console.log('启用网络摄像头');

			const stream = await navigator.mediaDevices.getUserMedia(
				{
					video :
					{
						deviceId : { ideal: device.deviceId },
						//frameRate : 15,
						...VIDEO_CONSTRAINS[resolution]
					}
				});

			track = stream.getVideoTracks()[0];
			//localStream.addTrack(track);
			localVideo.srcObject = stream;
			localStream = stream;
		}
		else
		{
			device = { label: 'external video' };

			const stream = await getExternalVideoStream();

			track = stream.getVideoTracks()[0].clone();
		}

		let encodings;
		let codec;
		const codecOptions =
		{
			videoGoogleStartBitrate : 1000	//视频开始比特率
			//videoGoogleStartBitrate : 300,	//视频开始比特率
			//videoGoogleMaxBitrate : 500,	//视频最大比特率
		};

		if (forceH264)	//强行H264
		{
			codec = mediasoupDevice.rtpCapabilities.codecs
				.find((c) => c.mimeType.toLowerCase() === 'video/h264');

			if (!codec)
			{
				throw new Error('desired H264 codec+configuration is not supported');
			}
		}
		else if (forceVP9)	//强行VP9
		{
			codec = mediasoupDevice.rtpCapabilities.codecs
				.find((c) => c.mimeType.toLowerCase() === 'video/vp9');

			if (!codec)
			{
				throw new Error('desired VP9 codec+configuration is not supported');
			}
		}

		if (useSimulcast)	//使用同步广播 
		{
			// 如果VP9是唯一可用的视频编解码器，则使用SVC。
			const firstVideoCodec = mediasoupDevice
				.rtpCapabilities
				.codecs
				.find((c) => c.kind === 'video');

			if (
				(forceVP9 && codec) ||
				firstVideoCodec.mimeType.toLowerCase() === 'video/vp9'
			)
			{
				encodings = WEBCAM_KSVC_ENCODINGS;
			}
			else
			{
				encodings = WEBCAM_SIMULCAST_ENCODINGS;
			}
		}

		/*codec = {
			kind       : 'video',
			mimeType   : 'video/VP8',
			clockRate  : 90000,
			parameters :
			{
				'x-google-start-bitrate' : 1000
			}
		};*/
		webcamProducer = await sendTransport.produce(	//传输将音频或视频轨道发送到mediasoup路由器
			{
				track,
				encodings,
				codecOptions,
				codec
			});

		webcamProducer.on('transportclose', () =>
		{
			webcamProducer = null;
		});

		webcamProducer.on('trackended', () =>
		{
			//disableWebcam()
			//	.catch(() => {});
		});
		console.log('webcamProducer:%o', webcamProducer.getStats());
		//localvideo_messageTextarea.value = localvideo_messageTextarea.value + 'codec: ' + webcamProducer.rtpSender.encodings + '\r\n';
	}
	catch (error)
	{
		console.log('enableWebcam() | failed:%o', error);

		if (track)
			track.stop();
	}
}


async function disableWebcam()	//关闭网络摄像头 
{
	console.log('disableWebcam()');

	if (!webcamProducer)
		return;

	webcamProducer.close();

	try
	{
		await peer.request(
			'closeProducer', { producerId: webcamProducer.id });
	}
	catch (error)
	{
		console.log('Error closing server-side webcam Producer:%o', error);
	}

	webcamProducer = null;
}


async function changeWebcam()	//转换网络摄像头 
{
	console.log('changeWebcam()');

	try
	{
		await updateWebcams();

		const array = Array.from(webcams.keys());
		const len = array.length;
		const deviceId = webcam.device ? webcam.device.deviceId : undefined;
		let idx = array.indexOf(deviceId);

		if (idx < len - 1)
			idx++;
		else
			idx = 0;

		webcam.device = webcams.get(array[idx]);

		console.log(
			'changeWebcam() | new selected webcam [device:%o]',
			webcam.device);

		// 将视频分辨率重置为高清
		webcam.resolution = 'hd';

		if (!webcam.device)
			throw new Error('no webcam devices');

		// 在请求新的视频之前关闭当前视频
		webcamProducer.track.stop();

		console.log('changeWebcam() | calling getUserMedia()');

		const stream = await navigator.mediaDevices.getUserMedia(
			{
				video :
				{
					deviceId : { exact: webcam.device.deviceId },
					...VIDEO_CONSTRAINS[webcam.resolution]
				}
			});

		const track = stream.getVideoTracks()[0];

		await webcamProducer.replaceTrack({ track });	//替换正在传输的音频或视频轨道
	}
	catch (error)
	{
		console.log('changeWebcam() | failed: %o', error);
	}
}


async function changeWebcamResolution()	//更改摄像头分辨率 
{
	console.log('changeWebcamResolution()');

	try
	{
		switch (webcam.resolution)
		{
			case 'qvga':
				webcam.resolution = 'vga';
				break;
			case 'vga':
				webcam.resolution = 'hd';
				break;
			case 'hd':
				webcam.resolution = 'qvga';
				break;
			default:
				webcam.resolution = 'hd';
		}

		console.log('changeWebcamResolution() | calling getUserMedia()');

		const stream = await navigator.mediaDevices.getUserMedia(
			{
				video :
				{
					deviceId : { exact: webcam.device.deviceId },
					...VIDEO_CONSTRAINS[webcam.resolution]
				}
			});

		const track = stream.getVideoTracks()[0];

		await webcamProducer.replaceTrack({ track });
	}
	catch (error)
	{
		console.log('changeWebcamResolution() | failed: %o', error);
	}
}


async function updateWebcams()	//更新摄像头
{
	console.log('_updateWebcams()');

	//  重置摄像头列表
	webcams = new Map();

	const devices = await navigator.mediaDevices.enumerateDevices(); //请求一个可用的媒体输入和输出设备的列表

	for (const device of devices)
	{
		if (device.kind !== 'videoinput')
			continue;

		webcams.set(device.deviceId, device);
	}

	const array = Array.from(webcams.values());
	const len = array.length;
	const currentWebcamId = webcam.device ? webcam.device.deviceId : undefined;

	console.log('_updateWebcams() [webcams:%o]', array);

	if (len === 0)
		webcam.device = null;
	else if (!webcams.has(currentWebcamId))
		webcam.device = array[0];
}


async function enableShare()	//开启共享 
{
	console.log('enableShare()');

	if (shareProducer)
		return;
	else if (webcamProducer)
		disableWebcam();

	if (!mediasoupDevice.canProduce('video'))
	{
		console.log('enableShare() | cannot produce video');

		return;
	}

	let track;

	try
	{
		console.log('enableShare() | calling getUserMedia()');

		const stream = await navigator.mediaDevices.getDisplayMedia(
			{
				audio : false,
				video :
				{
					displaySurface : 'monitor',			//显示器面
					logicalSurface : true,				//逻辑面
					cursor         : true,				//光标
					width          : { max: 1920 },
					height         : { max: 1080 },
					frameRate      : { max: 30 }
				}
			});

		if (!stream)
		{
			return;
		}

		track = stream.getVideoTracks()[0];

		let encodings;
		let codec;
		const codecOptions =
		{
			videoGoogleStartBitrate : 1000
		};

		if (forceH264)
		{
			codec = mediasoupDevice.rtpCapabilities.codecs
				.find((c) => c.mimeType.toLowerCase() === 'video/h264');

			if (!codec)
			{
				throw new Error('desired H264 codec+configuration is not supported');
			}
		}
		else if (forceVP9)
		{
			codec = mediasoupDevice.rtpCapabilities.codecs
				.find((c) => c.mimeType.toLowerCase() === 'video/vp9');

			if (!codec)
			{
				throw new Error('desired VP9 codec+configuration is not supported');
			}
		}

		if (useSharingSimulcast)
		{
			// 如果VP9是唯一可用的视频编解码器，则使用SVC。 
			const firstVideoCodec = mediasoupDevice
				.rtpCapabilities
				.codecs
				.find((c) => c.kind === 'video');

			if (
				(forceVP9 && codec) ||
				firstVideoCodec.mimeType.toLowerCase() === 'video/vp9'
			)
			{
				encodings = SCREEN_SHARING_SVC_ENCODINGS;
			}
			else
			{
				encodings = SCREEN_SHARING_SIMULCAST_ENCODINGS
					.map((encoding) => ({ ...encoding, dtx: true }));
			}
		}

		shareProducer = await sendTransport.produce(
			{
				track,
				encodings,
				codecOptions,
				codec,
				appData :
				{
					share : true
				}
			});

		shareProducer.on('transportclose', () =>
		{
			shareProducer = null;
		});

		shareProducer.on('trackended', () =>
		{
			disableShare()
				.catch(() => {});
		});
	}
	catch (error)
	{
		console.log('enableShare() | failed:%o', error);

		if (error.name !== 'NotAllowedError')
		{
			
		}

		if (track)
			track.stop();
	}
}


async function disableShare()	//关闭共享 
{
	console.log('disableShare()');

	if (!shareProducer)
		return;

	shareProducer.close();

	try
	{
		await peer.request(
			'closeProducer', { producerId: shareProducer.id });
	}
	catch (error)
	{
		console.log('Error closing server-side share Producer:%o', error);
	}

	shareProducer = null;
}


async function enableAudioOnly()	//仅启用音频 
{
	console.log('enableAudioOnly()');

	disableWebcam();

	for (const consumer of consumers.values())
	{
		if (consumer.kind !== 'video')
			continue;

		pauseConsumer(consumer);
	}

}


async function disableAudioOnly()	//仅关闭音频
{
	console.log('disableAudioOnly()');

	if (
		!webcamProducer &&
		(cookiesManager.getDevices() || {}).webcamEnabled
	)
	{
		enableWebcam();
	}

	for (const consumer of consumers.values())
	{
		if (consumer.kind !== 'video')
			continue;

		resumeConsumer(consumer);
	}

}


async function muteAudio()	//静音音频
{
	console.log('muteAudio()');
}

async function unmuteAudio()	//取消静音音频
{
	console.log('unmuteAudio()');
}


async function restartIce()	//重新启动Ice
{
	console.log('restartIce()');

	try
	{
		if (sendTransport)
		{
			const iceParameters = await protoo.request(
				'restartIce',
				{ transportId: sendTransport.id });

			await sendTransport.restartIce({ iceParameters });
		}

		if (recvTransport)
		{
			const iceParameters = await protoo.request(
				'restartIce',
				{ transportId: recvTransport.id });

			await recvTransport.restartIce({ iceParameters });
		}
	}
	catch (error)
	{
		console.log('restartIce() | failed:%o', error);
	}
}


async function setMaxSendingSpatialLayer(spatialLayer)	//设置最大发送空间层
{
	console.log('setMaxSendingSpatialLayer() [spatialLayer:%s]', spatialLayer);

	try
	{
		if (webcamProducer)
			await webcamProducer.setMaxSpatialLayer(spatialLayer);
		else if (shareProducer)
			await shareProducer.setMaxSpatialLayer(spatialLayer);
	}
	catch (error)
	{
		console.log('setMaxSendingSpatialLayer() | failed:%o', error);
	}
}
	

async function setConsumerPreferredLayers(consumerId, spatialLayer, temporalLayer)	//设置消费者首选层
{
	console.log(
		'setConsumerPreferredLayers() [consumerId:%s, spatialLayer:%s, temporalLayer:%s]',
		consumerId, spatialLayer, temporalLayer);

	try
	{
		await protoo.request(
			'setConsumerPreferredLayers', { consumerId, spatialLayer, temporalLayer });
	}
	catch (error)
	{
		console.log('setConsumerPreferredLayers() | failed:%o', error);
	}
}


async function setConsumerPriority(consumerId, priority)	//设置消费者优先级
{
	console.log(
		'setConsumerPriority() [consumerId:%s, priority:%d]',
		consumerId, priority);

	try
	{
		await protoo.request('setConsumerPriority', { consumerId, priority });
	}
	catch (error)
	{
		console.log('setConsumerPriority() | failed:%o', error);
	}
}


async function requestConsumerKeyFrame(consumerId)	//消费者请求关键帧
{
	console.log('requestConsumerKeyFrame() [consumerId:%s]', consumerId);

	try
	{
		await protoo.request('requestConsumerKeyFrame', { consumerId });
	}
	catch (error)
	{
		console.log('requestConsumerKeyFrame() | failed:%o', error);
	}
}







		














// //获取url参数
// function getQueryVariable(variable)
// {
//        var query = window.location.search.substring(1);
//        var vars = query.split("&");
//        for (var i=0;i<vars.length;i++) {
//                var pair = vars[i].split("=");
//                if(pair[0] == variable){return pair[1];}
//        }
//        return(false);
// }
function initializationData()
{
	mediasoupDevice = null;

	localStream = null;
	remoteStream = new MediaStream;

	sendTransport = null;
	recvTransport = null;

	micProducer = null;
	webcamProducer = null;
	shareProducer = null;

	externalVideo = null;
	externalVideoStream = null;

	webcams = new Map();
	consumers = new Map();
	dataConsumers = new Map();
}

function deviceInfo()
{
	window.BOWSER = bowser;
	const ua = navigator.userAgent;
	const browser = bowser.getParser(ua);
	let flag;

	if (browser.satisfies({ chrome: '>=0', chromium: '>=0' }))
		flag = 'chrome';
	else if (browser.satisfies({ firefox: '>=0' }))
		flag = 'firefox';
	else if (browser.satisfies({ safari: '>=0' }))
		flag = 'safari';
	else if (browser.satisfies({ opera: '>=0' }))
		flag = 'opera';
	else if (browser.satisfies({ 'microsoft edge': '>=0' }))
		flag = 'edge';
	else
		flag = 'unknown';

	return {
		flag,
		name    : browser.getBrowserName(),
		version : browser.getBrowserVersion()
	};
}